/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Helper functions to access data fields in Objects.
 */
#ifndef DALVIK_OO_OBJECTINLINES_H_
#define DALVIK_OO_OBJECTINLINES_H_

#include "../Inlines.h"
#include "Object.h"
#include "../Dalvik.h"

/*
 * Store a single value in the array, and if the value isn't null,
 * note in the write barrier.
 */
INLINE void dvmSetObjectArrayElement(const ArrayObject *obj, int index,
                                     Object *val) {
    ((Object **) (void *) (obj)->contents)[index] = val;
    if (val != NULL) {
        dvmWriteBarrierArray(obj, index, index + 1);
    }
}


/*
 * Field access functions.  Pass in the word offset from Field->byteOffset.
 *
 * We guarantee that long/double field data is 64-bit aligned, so it's safe
 * to access them with ldrd/strd on ARM.
 *
 * The VM treats all fields as 32 or 64 bits, so the field set functions
 * write 32 bits even if the underlying type is smaller.
 *
 * Setting Object types to non-null values includes a call to the
 * write barrier.
 */
#define BYTE_OFFSET(_ptr, _offset)  ((void*) (((u1*)(_ptr)) + (_offset)))

INLINE JValue *dvmFieldPtr(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset));
}

INLINE bool dvmGetFieldBoolean(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->z;
}

INLINE s1 dvmGetFieldByte(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->b;
}

INLINE s2 dvmGetFieldShort(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->s;
}

INLINE u2 dvmGetFieldChar(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->c;
}

INLINE s4 dvmGetFieldInt(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->i;
}

INLINE s8 dvmGetFieldLong(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->j;
}

INLINE float dvmGetFieldFloat(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->f;
}

INLINE double dvmGetFieldDouble(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->d;
}

INLINE Object *dvmGetFieldObject(const Object *obj, int offset) {
    return ((JValue *) BYTE_OFFSET(obj, offset))->l;
}

INLINE bool dvmGetFieldBooleanVolatile(const Object *obj, int offset) {
    s4 *ptr = &((JValue *) BYTE_OFFSET(obj, offset))->i;
    return (bool) android_atomic_acquire_load(ptr);
}

INLINE s1 dvmGetFieldByteVolatile(const Object *obj, int offset) {
    s4 *ptr = &((JValue *) BYTE_OFFSET(obj, offset))->i;
    return (s1) android_atomic_acquire_load(ptr);
}

INLINE s2 dvmGetFieldShortVolatile(const Object *obj, int offset) {
    s4 *ptr = &((JValue *) BYTE_OFFSET(obj, offset))->i;
    return (s2) android_atomic_acquire_load(ptr);
}

INLINE u2 dvmGetFieldCharVolatile(const Object *obj, int offset) {
    s4 *ptr = &((JValue *) BYTE_OFFSET(obj, offset))->i;
    return (u2) android_atomic_acquire_load(ptr);
}

INLINE s4 dvmGetFieldIntVolatile(const Object *obj, int offset) {
    s4 *ptr = &((JValue *) BYTE_OFFSET(obj, offset))->i;
    return android_atomic_acquire_load(ptr);
}

INLINE float dvmGetFieldFloatVolatile(const Object *obj, int offset) {
    union {
        s4 ival;
        float fval;
    } alias;
    s4 *ptr = &((JValue *) BYTE_OFFSET(obj, offset))->i;
    alias.ival = android_atomic_acquire_load(ptr);
    return alias.fval;
}

INLINE s8 dvmGetFieldLongVolatile(const Object *obj, int offset) {
    const s8 *addr = (const s8 *) BYTE_OFFSET(obj, offset);
    s8 val = dvmQuasiAtomicRead64(addr);
    ANDROID_MEMBAR_FULL();
    return val;
}

INLINE double dvmGetFieldDoubleVolatile(const Object *obj, int offset) {
    union {
        s8 lval;
        double dval;
    } alias;
    const s8 *addr = (const s8 *) BYTE_OFFSET(obj, offset);
    alias.lval = dvmQuasiAtomicRead64(addr);
    ANDROID_MEMBAR_FULL();
    return alias.dval;
}

INLINE Object *dvmGetFieldObjectVolatile(const Object *obj, int offset) {
    Object **ptr = &((JValue *) BYTE_OFFSET(obj, offset))->l;
    return (Object *) android_atomic_acquire_load((int32_t *) ptr);
}

INLINE void dvmSetFieldBoolean(Object *obj, int offset, bool val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->i = val;
}

INLINE void dvmSetFieldByte(Object *obj, int offset, s1 val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->i = val;
}

INLINE void dvmSetFieldShort(Object *obj, int offset, s2 val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->i = val;
}

INLINE void dvmSetFieldChar(Object *obj, int offset, u2 val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->i = val;
}

INLINE void dvmSetFieldInt(Object *obj, int offset, s4 val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->i = val;
}

INLINE void dvmSetFieldFloat(Object *obj, int offset, float val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->f = val;
}

INLINE void dvmSetFieldLong(Object *obj, int offset, s8 val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->j = val;
}

INLINE void dvmSetFieldDouble(Object *obj, int offset, double val) {
    ((JValue *) BYTE_OFFSET(obj, offset))->d = val;
}

INLINE void dvmSetFieldObject(Object *obj, int offset, Object *val) {
    JValue *lhs = (JValue *) BYTE_OFFSET(obj, offset);
    lhs->l = val;
    if (val != NULL) {
        dvmWriteBarrierField(obj, &lhs->l);
    }
}

INLINE void dvmSetFieldIntVolatile(Object *obj, int offset, s4 val) {
    s4 *ptr = &((JValue *) BYTE_OFFSET(obj, offset))->i;
    /*
     * TODO: add an android_atomic_synchronization_store() function and
     * use it in the 32-bit volatile set handlers.  On some platforms we
     * can use a fast atomic instruction and avoid the barriers.
     */
    ANDROID_MEMBAR_STORE();
    *ptr = val;
    ANDROID_MEMBAR_FULL();
}

INLINE void dvmSetFieldBooleanVolatile(Object *obj, int offset, bool val) {
    dvmSetFieldIntVolatile(obj, offset, val);
}

INLINE void dvmSetFieldByteVolatile(Object *obj, int offset, s1 val) {
    dvmSetFieldIntVolatile(obj, offset, val);
}

INLINE void dvmSetFieldShortVolatile(Object *obj, int offset, s2 val) {
    dvmSetFieldIntVolatile(obj, offset, val);
}

INLINE void dvmSetFieldCharVolatile(Object *obj, int offset, u2 val) {
    dvmSetFieldIntVolatile(obj, offset, val);
}

INLINE void dvmSetFieldFloatVolatile(Object *obj, int offset, float val) {
    union {
        s4 ival;
        float fval;
    } alias;
    alias.fval = val;
    dvmSetFieldIntVolatile(obj, offset, alias.ival);
}

INLINE void dvmSetFieldLongVolatile(Object *obj, int offset, s8 val) {
    s8 *addr = (s8 *) BYTE_OFFSET(obj, offset);
    dvmQuasiAtomicSwap64Sync(val, addr);
}

INLINE void dvmSetFieldDoubleVolatile(Object *obj, int offset, double val) {
    union {
        s8 lval;
        double dval;
    } alias;
    alias.dval = val;
    dvmSetFieldLongVolatile(obj, offset, alias.lval);
}

INLINE void dvmSetFieldObjectVolatile(Object *obj, int offset, Object *val) {
    Object **ptr = &((JValue *) BYTE_OFFSET(obj, offset))->l;
    ANDROID_MEMBAR_STORE();
    *ptr = val;
    ANDROID_MEMBAR_FULL();
    if (val != NULL) {
        dvmWriteBarrierField(obj, ptr);
    }
}

/*
 * Static field access functions.
 */
INLINE JValue *dvmStaticFieldPtr(const StaticField *sfield) {
    return (JValue *) &sfield->value;
}

INLINE bool dvmGetStaticFieldBoolean(const StaticField *sfield) {
    return sfield->value.z;
}

INLINE s1 dvmGetStaticFieldByte(const StaticField *sfield) {
    return sfield->value.b;
}

INLINE s2 dvmGetStaticFieldShort(const StaticField *sfield) {
    return sfield->value.s;
}

INLINE u2 dvmGetStaticFieldChar(const StaticField *sfield) {
    return sfield->value.c;
}

INLINE s4 dvmGetStaticFieldInt(const StaticField *sfield) {
    return sfield->value.i;
}

INLINE float dvmGetStaticFieldFloat(const StaticField *sfield) {
    return sfield->value.f;
}

INLINE s8 dvmGetStaticFieldLong(const StaticField *sfield) {
    return sfield->value.j;
}

INLINE double dvmGetStaticFieldDouble(const StaticField *sfield) {
    return sfield->value.d;
}

INLINE Object *dvmGetStaticFieldObject(const StaticField *sfield) {
    return sfield->value.l;
}

INLINE bool dvmGetStaticFieldBooleanVolatile(const StaticField *sfield) {
    const s4 *ptr = &(sfield->value.i);
    return (bool) android_atomic_acquire_load((s4 *) ptr);
}

INLINE s1 dvmGetStaticFieldByteVolatile(const StaticField *sfield) {
    const s4 *ptr = &(sfield->value.i);
    return (s1) android_atomic_acquire_load((s4 *) ptr);
}

INLINE s2 dvmGetStaticFieldShortVolatile(const StaticField *sfield) {
    const s4 *ptr = &(sfield->value.i);
    return (s2) android_atomic_acquire_load((s4 *) ptr);
}

INLINE u2 dvmGetStaticFieldCharVolatile(const StaticField *sfield) {
    const s4 *ptr = &(sfield->value.i);
    return (u2) android_atomic_acquire_load((s4 *) ptr);
}

INLINE s4 dvmGetStaticFieldIntVolatile(const StaticField *sfield) {
    const s4 *ptr = &(sfield->value.i);
    return android_atomic_acquire_load((s4 *) ptr);
}

INLINE float dvmGetStaticFieldFloatVolatile(const StaticField *sfield) {
    union {
        s4 ival;
        float fval;
    } alias;
    const s4 *ptr = &(sfield->value.i);
    alias.ival = android_atomic_acquire_load((s4 *) ptr);
    return alias.fval;
}

INLINE s8 dvmGetStaticFieldLongVolatile(const StaticField *sfield) {
    const s8 *addr = &sfield->value.j;
    s8 val = dvmQuasiAtomicRead64(addr);
    ANDROID_MEMBAR_FULL();
    return val;
}

INLINE double dvmGetStaticFieldDoubleVolatile(const StaticField *sfield) {
    union {
        s8 lval;
        double dval;
    } alias;
    const s8 *addr = &sfield->value.j;
    alias.lval = dvmQuasiAtomicRead64(addr);
    ANDROID_MEMBAR_FULL();
    return alias.dval;
}

INLINE Object *dvmGetStaticFieldObjectVolatile(const StaticField *sfield) {
    Object *const *ptr = &(sfield->value.l);
    return (Object *) android_atomic_acquire_load((int32_t *) ptr);
}

INLINE void dvmSetStaticFieldBoolean(StaticField *sfield, bool val) {
    sfield->value.i = val;
}

INLINE void dvmSetStaticFieldByte(StaticField *sfield, s1 val) {
    sfield->value.i = val;
}

INLINE void dvmSetStaticFieldShort(StaticField *sfield, s2 val) {
    sfield->value.i = val;
}

INLINE void dvmSetStaticFieldChar(StaticField *sfield, u2 val) {
    sfield->value.i = val;
}

INLINE void dvmSetStaticFieldInt(StaticField *sfield, s4 val) {
    sfield->value.i = val;
}

INLINE void dvmSetStaticFieldFloat(StaticField *sfield, float val) {
    sfield->value.f = val;
}

INLINE void dvmSetStaticFieldLong(StaticField *sfield, s8 val) {
    sfield->value.j = val;
}

INLINE void dvmSetStaticFieldDouble(StaticField *sfield, double val) {
    sfield->value.d = val;
}

INLINE void dvmSetStaticFieldObject(StaticField *sfield, Object *val) {
    sfield->value.l = val;
    if (val != NULL) {
        dvmWriteBarrierField(sfield->clazz, &sfield->value.l);
    }
}

INLINE void dvmSetStaticFieldIntVolatile(StaticField *sfield, s4 val) {
    s4 *ptr = &sfield->value.i;
    ANDROID_MEMBAR_STORE();
    *ptr = val;
    ANDROID_MEMBAR_FULL();
}

INLINE void dvmSetStaticFieldBooleanVolatile(StaticField *sfield, bool val) {
    dvmSetStaticFieldIntVolatile(sfield, val);
}

INLINE void dvmSetStaticFieldByteVolatile(StaticField *sfield, s1 val) {
    dvmSetStaticFieldIntVolatile(sfield, val);
}

INLINE void dvmSetStaticFieldShortVolatile(StaticField *sfield, s2 val) {
    dvmSetStaticFieldIntVolatile(sfield, val);
}

INLINE void dvmSetStaticFieldCharVolatile(StaticField *sfield, u2 val) {
    dvmSetStaticFieldIntVolatile(sfield, val);
}

INLINE void dvmSetStaticFieldFloatVolatile(StaticField *sfield, float val) {
    union {
        s4 ival;
        float fval;
    } alias;
    alias.fval = val;
    dvmSetStaticFieldIntVolatile(sfield, alias.ival);
}

INLINE void dvmSetStaticFieldLongVolatile(StaticField *sfield, s8 val) {
    s8 *addr = &sfield->value.j;
    dvmQuasiAtomicSwap64Sync(val, addr);
}

INLINE void dvmSetStaticFieldDoubleVolatile(StaticField *sfield, double val) {
    union {
        s8 lval;
        double dval;
    } alias;
    alias.dval = val;
    dvmSetStaticFieldLongVolatile(sfield, alias.lval);
}

INLINE void dvmSetStaticFieldObjectVolatile(StaticField *sfield, Object *val) {
    Object **ptr = &(sfield->value.l);
    ANDROID_MEMBAR_STORE();
    *ptr = val;
    ANDROID_MEMBAR_FULL();
    if (val != NULL) {
        dvmWriteBarrierField(sfield->clazz, &sfield->value.l);
    }
}

#endif  // DALVIK_OO_OBJECTINLINES_H_
